xlv.obj.sqlRowset={
	extend:function(){
		this.extend('xlv.obj.rowset');
	},
	implement:function(){
	},
	create:function(tag){
		var div=document.createElement((tag||'div'));
		div.setAttribute('xlvobject','xlv.obj.sqlRowset');
		return div;			
	},
	properties:{
		name:null,
		tableName:null,
		procedureName:null,
		propXPath:'properties',
		metaXPath:'properties/sql-query/return/metadata',
		dataXPath:'properties/sql-query/return/data',
		rowName:'r',
		autoPost:'none'//post, none,default is none
	},
	method:{
		initial:function(){
			this.$super('xlv.obj.rowset.initial');
			if(this.$P.metaXPath)this.$P.metaXPath=this.$P.metaXPath.replace(/\./g,'/');
		},
		start:function(){
			if(!this.$V.__started){
				var arg=null;
				if(this.$P.tableName){
					arg=this.xml_createElement('get-properties');
					arg.appendChild(this.xml_createElement('table-name',this.$P.tableName));
				}else if(this.$P.procedureName){
					arg=this.xml_createElement('get-properties');
					arg.appendChild(this.xml_createElement('procedure-name',this.$P.procedureName));
				}
				if(this.$P.objectURL) {
					this.$V.lastMethodName='get-properties';
					this.queueTimeout((function(){this.request(null, 'get-properties', arg);}).bind(this));
				}
				this.$V.__started=true;
			}else if(this.$V.__mvcArray){
					var mvc_array=this.$V.__mvcArray;
					this.$V.__mvcArray=null;
					this.$V.__queueNotifyMVC=true;
					var memo=this.createMemoObject({type:'create',methods:'start',from:this.$P.name,to:this.$P.objectURL,cancelable:false});
					this.notifyMVC('beforeModelChanged','setChanged',memo,mvc_array);
					this.notifyMVC('modelChanged','setChanged',memo,mvc_array);
					this.notifyMVC('afterModelChanged','setChanged',memo,mvc_array);
					memo=this.createMemoObject({type:'cursorMove',methods:'start',from:this.$V.currentNode,to:this.$V.currentNode});
					this.notifyMVC('beforeModelChanged','cursorMove',memo,mvc_array);
					this.notifyMVC('modelChanged','cursorMove',memo,mvc_array);
					this.notifyMVC('afterModelChanged','cursorMove',memo,mvc_array);
					this.$V.__queueNotifyMVC=false;
			}
		},
		dispose:function(){
			this._disposeResultset();
			this.$super('xlv.obj.rowset.dispose');
		},
		_disposeResultset:function(){
			if(!this.xml_XMLReady()) return;
			var data=this.xml_selectSingleNode(this.$P.dataXPath);
			if(!data || data.getAttributeNode('eof')!=null||!data.getAttributeNode('rowTotal')||!data.hasChildNodes()) return;
			var dispose=this.xml_createElement('dispose-resultset');
			var n=this.xml_createElement('resultset-id');
			this.xml_setNodeValue(n,data.getAttribute('resultsetId'));
			dispose.appendChild(n);
			this.request(null,'dispose-resultset', dispose);
		},
		/*****/
		webObjectResponse:function(){
			var res_method_names=this.xml_responseMethodNames();
			if(res_method_names=='get-properties') {
				if(this.$P.objectURL && this.$P.methodName){
					if(this.$P.methodName=='sql-query')	this.queueTimeout((function(){this.query();}).bind(this));
					else this.queueTimeout((function(){if(!this.webMethod(this.$P.methodName))this._notifiyReady();}).bind(this));
				}else {
					var memo=this.createMemoObject({type:'create',methods:res_method_names,from:this.$P.name,to:this.$P.objectURL,cancelable:false});
					this.notifyMVC('beforeModelChanged','setChanged',memo);
					this.notifyMVC('modelChanged','setChanged',memo);
					this.notifyMVC('afterModelChanged','setChanged',memo);
					this._notifiyReady();
				}
				return;
			}
			var memo;
			var sql_query=this.xml_selectSingleNode('sql-query' ,this.xml_properties.responseXML.documentElement);
			if(sql_query!=null){
				memo=this.createMemoObject({type:'create',methods:res_method_names,cancelable:false});
				this.notifyMVC('beforeModelChanged','setChanged',memo);
				var query_data=this.xml_selectSingleNode('return/data',sql_query);
				if(query_data!=null){
					var data=this.xml_selectSingleNode(this.$P.dataXPath);
					data.parentNode.replaceChild(this.xml_adoptNode(query_data),data);
				}
				var query_meta=this.xml_selectSingleNode('return/metadata',sql_query);
				if(query_meta!=null){
					var meta=this.xml_selectSingleNode(this.$P.metaXPath);
					meta.parentNode.replaceChild(this.xml_adoptNode(query_meta),meta);
				}
				var r=this.xml_selectSingleNode(this.$P.propXPath+'/sql-query/record-tag-name');
				if(r) {
					r=this.xml_getNodeValue(r).strip();
					if(r&&r.length>0)this.$P.rowName=r;
				}
				this.$V.updated=false;
				this.$V.currentNode=this.xml_selectSingleNode(this.$P.dataXPath+'/'+this.$P.rowName+'[1]');
				this.$V.traceNode=this.$V.currentNode;
				this.notifyMVC('modelChanged','setChanged',memo);
				this.notifyMVC('afterModelChanged','setChanged',memo);
				this._moveToNode(this.$V.currentNode);
			}
			var sql_fetch=this.xml_selectSingleNode('sql-fetch',this.xml_properties.responseXML.documentElement);
			if(sql_fetch!=null){
				this.$V.fetching=false;
				var fetch_data=this.xml_selectSingleNode('return/data',sql_fetch);
				var data=this.xml_selectSingleNode(this.$P.dataXPath);
				data.setAttribute('rowTotal',parseInt(fetch_data.getAttribute('rowCount'))+parseInt(data.getAttribute('rowTotal')));
				if(fetch_data.getAttributeNode('eof') != null) data.setAttribute('eof','true');
				var fetch_rows=this.xml_selectNodes('child::*',fetch_data);
				memo=this.createMemoObject({type:'append',methods:res_method_names,cancelable:false});
				memo.from=this.rows()+1;
				memo.to=memo.from+fetch_rows.length-1;
				this.notifyMVC('beforeModelChanged','setChanged',memo);
				for(var i=0;i<fetch_rows.length;++i) data.appendChild(this.xml_adoptNode(fetch_rows[i]));
				this.notifyMVC('modelChanged','setChanged',memo);
				this.notifyMVC('afterModelChanged','setChanged',memo);
			}
			var sql_insert=this.xml_selectSingleNode('sql-insert',this.xml_properties.responseXML.documentElement);
			if(sql_insert != null){
				var ins_rows=this.xml_selectNodes(this.$P.dataXPath+'/'+this.$P.rowName+'[@xlv_ins]');
				var auto_flds=this.xml_selectNodes(this.$P.metaXPath+'/'+'columns/child::*[@autoIncrement=\"true\"]');
				var ret_rows=this.xml_selectNodes('return/data/'+this.$P.rowName,sql_insert);
				for(var i=0;i<ret_rows.length;++i){
					var gkey=this.xml_selectNodes('child::*',ret_rows[i]);
					for(var k=0;k<gkey.length && k<auto_flds.length;++k){
						var r=this.xml_selectSingleNode(auto_flds[k].nodeName, ins_rows[i]);
						this.xml_setNodeValue(r,this.xml_getNodeValue(gkey[k]));
						memo=this.createMemoObject({type:'update',methods:res_method_names,src:r,from:r,cancelable:false});
						this.notifyMVC('modelChanged','nodeChanged',memo);
					}
				}
				for(var n=0;n<ins_rows.length;++n){
					memo=this.createMemoObject({type:'inserted',methods:res_method_names,cancelable:false});
					memo.src=memo.from=memo.to=ins_rows[n];
					this.notifyMVC('beforeModelChanged','rowChanged',memo);
					ins_rows[n].removeAttribute('xlv_ins');
					var old_tags=this.xml_selectNodes('child::*[@xlv_old]',ins_rows[n]);
					for(var j=0;j<old_tags.length;++j) old_tags[j].removeAttribute('xlv_old');
					this.notifyMVC('modelChanged','rowChanged',memo);
					this.notifyMVC('afterModelChanged','rowChanged',memo);
				}
			}
			var sql_update=this.xml_selectSingleNode('sql-update',this.xml_properties.responseXML.documentElement);
			if(sql_update != null){
				var upd_rows=this.xml_selectNodes(this.$P.dataXPath+'/'+this.$P.rowName+'[@xlv_upd]');
				for(var i=0;i<upd_rows.length;++i){
					memo=this.createMemoObject({type:'updated',methods:res_method_names,cancelable:false});
					memo.src=memo.from=memo.to=upd_rows[i];
					this.notifyMVC('beforeModelChanged','rowChanged',memo);
					var olds=this.xml_selectNodes('child::*[@xlv_old]',upd_rows[i]);
					for(var d=0;d<olds.length;++d) olds[d].removeAttribute('xlv_old');
					upd_rows[i].removeAttribute('xlv_upd');
					this.notifyMVC('modelChanged','rowChanged',memo);
					this.notifyMVC('afterModelChanged','rowChanged',memo);
				}
			}
			var current_row=this.$V.currentNode;
			var sql_delete=this.xml_selectSingleNode('sql-delete',this.xml_properties.responseXML.documentElement);
			if(sql_delete!=null){
				var del_rows=this.xml_selectNodes(this.$P.dataXPath+'/'+this.$P.rowName+'[@xlv_del]');
				for(var i=0;i<del_rows.length;++i) {
					if(current_row==del_rows[i]) current_row=this.advanceRow(current_row);
					memo=this.createMemoObject({type:'removed',methods:res_method_names,src:del_rows[i],from:del_rows[i]});
					this.notifyMVC('beforeModelChanged','rowChanged',memo);
					del_rows[i].parentNode.removeChild(del_rows[i]);
					this.notifyMVC('modelChanged','rowChanged',memo);
					this.notifyMVC('afterModelChanged','rowChanged',memo);
				}
			}
			
			var sql_state=this.xml_selectSingleNode('sql-statement',this.xml_properties.responseXML.documentElement);
			if(sql_state!=null){
				memo=this.createMemoObject({type:'create',methods:res_method_names,src:this,cancelable:false});
				this.notifyMVC('beforeModelChanged','setChanged',memo);
				var state_data=this.xml_selectSingleNode('return/data',sql_state);
				if(state_data!=null){
					var data=this.xml_selectSingleNode('properties/sql-statement/return/data');
					data.parentNode.replaceChild(this.xml_adoptNode(state_data),data);
				}
				this.notifyMVC('modelChanged','setChanged',memo);
				this.notifyMVC('afterModelChanged','setChanged',memo);
			}
			var call_procedure=this.xml_selectSingleNode('call-procedure',this.xml_properties.responseXML.documentElement);
			if(call_procedure!=null){
				memo=this.createMemoObject({type:'create',methods:res_method_names,src:this,cancelable:false});
				this.notifyMVC('beforeModelChanged','setChanged',memo);
				var call_data=this.xml_selectSingleNode('return/data',call_procedure);
				if(call_data!=null){
					var data=this.xml_selectSingleNode(this.$P.dataXPath);
					data.parentNode.replaceChild(this.xml_adoptNode(call_data),data);
				}
				this.$V.updated=false;
				this.$V.currentNode=this.xml_selectSingleNode(this.$P.dataXPath+'/'+this.$P.rowName+'[1]');
				this.$V.traceNode=this.$V.currentNode;
				this.notifyMVC('modelChanged','setChanged',memo);
				this.notifyMVC('afterModelChanged','setChanged',memo);
				this._moveToNode(this.$V.currentNode);
			}
			this.$V.updated=false;
			if(this.$V.autoPostMoveToNode){
				var next=this.$V.autoPostMoveToNode;
				this.$V.autoPostMoveToNode=null;
				this._moveToNode(next);
			}else if(current_row!=this.$V.currentNode)this._moveToNode(current_row);
			//
			this._notifiyReady();
		},
		getValue:function(xpath,nth){
			var root_node=(arguments.length>2&&Object.isElement(arguments[2]))?arguments[2]:
				(arguments.length>1 && Object.isElement(arguments[1])) ? arguments[1]:null;
			nth=(root_node)? null:nth;
			//
			var node=this.getNode(xpath,nth,root_node);
			this._fetchCheck(nth); 
			return node ? this.xml_getNodeValue(node): '';
		},
		setValue:function(value,xpath,nth){
			var root_node=(arguments.length>3&&Object.isElement(arguments[3]))?arguments[3]:
				(arguments.length>2 && Object.isElement(arguments[2]))?arguments[2]:null;
			nth=(root_node)? null:nth;
			//
			var node=this.getNode(xpath,nth,root_node);
			if(node){
				if(!nth&&(xpath.startsWith(this.$P.propXPath)||xpath.startsWith(this.$P.metaXPath))){
					this.xml_setNodeValue(node,value);
					return;
				}
				var old=this.xml_getNodeValue(node);
				if(old==value)return;
				if(node.getAttributeNode('xlv_old')==null)node.setAttribute('xlv_old',old);
				if(node.parentNode.attributes.length==0) node.parentNode.setAttribute('xlv_upd','1');
				this.xml_setNodeValue(node,value);
				this.$V.updated=true;
				var memo=this.createMemoObject({type:'update',methods:'setValue',src:node,from:old,to:value,'value':value,cancelable:false});
				this.notifyMVC('modelChanged','nodeChanged',memo);
			}
		},
		/****/
		_fetchCheck:function(nth_row){
			var row_need;
			if(!this.$V.fetching && !this.isCompleted() && (row_need=(nth_row||this.rowNumber()))>=this.rows()&&row_need>0) {
				if(!this.$V.__alreadyInQueue){
					this.$V.__alreadyInQueue=true;
					this.queueTimeout((function(){this.fetch();this.$V.__alreadyInQueue=false;}).bind(this));
				}
			}
		},
		fetch:function(row_fetch){
			var sql_fetch=this.xml_selectSingleNode(this.$P.propXPath+'/sql-fetch');
			if(!sql_fetch||this.isCompleted())return false; 
			if(this.$V.fetching)return true;
			sql_fetch=sql_fetch.cloneNode(true);
			this.$V.fetching=true;
			var data=this.xml_selectSingleNode(this.$P.dataXPath);
			var rid=this.xml_selectSingleNode('resultset-id',sql_fetch);
			this.xml_setNodeValue(rid,data.getAttribute('resultsetId'));
			if(row_fetch){
				var rowf=this.xml_selectSingleNode('row-fetch',sql_fetch);
				if(rowf)this.xml_setNodeValue(rowf, row_fetch);
			}
			this.request(null,'sql-fetch',sql_fetch);
			return true;
		},
		fetchable:function(){
			return !this.isCompleted();
		},
		updatable:function(row){
			row=(row||this.$V.currentNode);
			return !this.isDeletedRow(row);
		},
		_updatePost:function(row){
			row=(row||this.$V.currentNode);
			if(!this.isUpdatedRow(row))return null;
			var sqlupd=this.xml_selectSingleNode(this.$P.propXPath+'/sql-update').cloneNode(true);
			var sqlupd_keys=this.xml_selectNodes('key-fields/child::*',sqlupd);
			for(var k=0;k<sqlupd_keys.length;++k){
				var r=this.xml_selectSingleNode(sqlupd_keys[k].nodeName, row);
				var data=(r.getAttributeNode('xlv_old')!=null)?r.getAttribute('xlv_old'): this.xml_getNodeValue(r);
				this.xml_setNodeValue(sqlupd_keys[k],data);
			}
			//update-fields
			var sqlupd_flds=this.xml_selectSingleNode('update-fields',sqlupd);
			var row_flds=this.xml_selectNodes('child::*[@xlv_old]',row);
			for(var f=0;f<row_flds.length;++f){
				var e=this.xml_createElement(row_flds[f].nodeName);
				this.xml_setNodeValue(e,this.xml_getNodeValue(row_flds[f]).strip());
				var meta=this.xml_selectSingleNode(this.$P.metaXPath+'/columns/'+e.nodeName);
				if(meta) e.setAttribute('dataType',meta.getAttribute('dataType'));
				sqlupd_flds.appendChild(e);
			}
			return row_flds.length > 0 ? sqlupd : null;
		},
		postable:function(){
			return true;//return this.isDirtyRow();//this.$V.currentState=='normal';
		},
		post:function(){
			if(!this.postable()) return false;
			var methods='',ns;
			//update
			ns=this.xml_selectNodes(this.$P.dataXPath+'/'+this.$P.rowName+'[@xlv_upd]');
			for(var i=0;i<ns.length;++i) {
				var upd=this._updatePost(ns[i]);
				if(upd)	methods += this._makeMethodArguments('sql-update',upd);
			}
			//delete
			ns=this.xml_selectNodes(this.$P.dataXPath+'/'+this.$P.rowName+'[@xlv_del]');
			for(var i=0;i<ns.length;++i) {
				var del=this._removePost(ns[i]);
				if(del) methods += this._makeMethodArguments('sql-delete',del);
			}
			//insert
			ns=this.xml_selectNodes(this.$P.dataXPath+'/'+this.$P.rowName+'[@xlv_ins]');
			for(var i=0;i<ns.length;++i) {
				var ins=this._insertPost(ns[i]);
				if(ins)methods += this._makeMethodArguments('sql-insert',ins);
			}
			return (methods.length > 0)?this.requestMethods(null,methods):false;
		},
		removeable:function(row){
			row=(row||this.$V.currentNode);
			return (row!=null && !this.isInsertedRow(row));
		},
		_removePost:function(row){
			row=(row||this.$V.currentNode);
			if(!this.isRemovedRow(row))return null;
			var sqldel=this.xml_selectSingleNode(this.$P.propXPath+'/sql-delete').cloneNode(true);
			var sqldel_keys=this.xml_selectNodes('key-fields/child::*',sqldel);
			for(var k=0;k<sqldel_keys.length;++k){
				var f=this.xml_selectSingleNode(sqldel_keys[k].nodeName, row);
				var data=(f.getAttributeNode('xlv_old')!=null)?f.getAttribute('xlv_old'):this.xml_getNodeValue(f);
				this.xml_setNodeValue(sqldel_keys[k],data);
			}	
			return sqldel_keys.length >0 ? sqldel:null;
		},
		insertable:function(){
			return this.xml_selectSingleNode(this.$P.propXPath+'/sql-insert')!=null;
		},
		makeBlankRow:function(){
			var r=this.xml_createElement(this.$P.rowName);
			var meta=this.xml_selectNodes(this.$P.metaXPath+'/columns/child::*');
			var n;
			for(var m=0;m<meta.length;++m){
				r.appendChild(n=this.xml_createElement(meta[m].nodeName));
				n.setAttribute('dataType',meta[m].getAttribute('dataType'));
			}
			return r;
		},
		_insertPost:function(row){
			row=(row||this.$V.currentNode);
			if(!this.isInsertedRow(row))return null;
			var sqlins=this.xml_selectSingleNode(this.$P.propXPath+'/sql-insert').cloneNode(true);
			var sqlins_flds=this.xml_selectSingleNode('fields',sqlins);
			var row_flds=row.childNodes;
			for(var i=0;i<row_flds.length;++i){
				var value=this.xml_getNodeValue(row_flds[i]).strip();
				if(value=='')continue;
				sqlins_flds.appendChild(row_flds[i].cloneNode(true));
			}
			return row_flds.length>0 ? sqlins : null;
		},
		query:function(){
			if(!this.queryable()) return false;
			var qry=this.xml_selectSingleNode(this.$P.propXPath+'/sql-query');
			//
			var id=this.xml_createElement('resultset-id');
			var data=this.xml_selectSingleNode(this.$P.dataXPath);
			this.xml_setNodeValue(id,data.getAttribute('resultsetId'));
			//
			var return_node=this.xml_selectSingleNode('return',qry);
			var parent_node=return_node.parentNode;
			parent_node.removeChild(return_node);
			var clone_qry=qry.cloneNode(true);
			clone_qry.appendChild(id);
			//
			parent_node.appendChild(return_node);
			this.request(null,'sql-query',clone_qry);
			return true;
		},
		resetQueryParameters:function(){
		},
		queryable:function(){
			return this.xml_selectSingleNode(this.$P.propXPath+'/sql-query')!=null;
		},
		callProcedure:function(){
			if(!this.callProcedurable()) return false;
			var call=this.xml_selectSingleNode(this.$P.propXPath+'/call-procedure');
			this.request(null,'call-procedure',call.cloneNode(true));
			return true;
		},
		callProcedurable:function(){
			var p_name= this.xml_selectSingleNode(this.$P.propXPath+'/call-procedure/name');
			return p_name!=null && p_name.strip().length > 0;
		},
		isCompleted:function(){
			var data=this.xml_selectSingleNode(this.$P.dataXPath);
			return (data && data.getAttributeNode('eof') != null);
		}
	}
};

